package com.cmy.mylog.aop;

import cn.hutool.core.util.BooleanUtil;
import com.cmy.common.utils.MyJsonUtils;
import com.cmy.common.utils.ThrowableUtil;
import com.cmy.mylog.annotations.MyLog;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author:Cmy
 * @Date:2023-11-13 14:02
 */
@Slf4j
@Data
public class MyLogAspect implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) {
        long startTime = System.currentTimeMillis();
        List<Object> logObjs = new ArrayList<>();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String uri = "";
        String methodType = "";
        String remoteAddr = "";
        if (attributes!=null){//说明是controller层方法
            HttpServletRequest request = attributes.getRequest();
            uri = request.getRequestURI();
            methodType = request.getMethod();
            remoteAddr = request.getRemoteAddr();
        }
        Method method = invocation.getMethod();
        Object[] args = invocation.getArguments();
        //过滤不可序列化参数
        List<Object> paramList = Arrays
                .stream(args)
                .filter(o -> !(o instanceof ServletRequest || o instanceof ServletResponse || o instanceof MultipartFile ))
                .collect(Collectors.toList());
        MyLog myLog = method.getAnnotation(MyLog.class);
        String desc = "";
        if (myLog!=null){
            desc = myLog.desc();
        }
        // 如果注解没有参数，获取方法名称
        String descLog = "".equals(desc) ? method.getName() : desc;
        String requestArgJsonStr = MyJsonUtils.toJsonString(paramList);

        StringBuilder paramLogBuilder = new StringBuilder("\n");
        paramLogBuilder.append(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Params-Field>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
        if (StringUtils.isNotBlank(uri)){
            paramLogBuilder.append("URI             : {}\n");
            logObjs.add(uri);
        }
        if (StringUtils.isNotBlank(methodType)){
            paramLogBuilder.append("HTTP Method Type: {}\n");
            logObjs.add(methodType);
        }
        if (StringUtils.isNotBlank(remoteAddr)){
            paramLogBuilder.append("RemoteAddr      : {}\n");
            logObjs.add(remoteAddr);
        }
        paramLogBuilder.append("Method          : {}\n");
        logObjs.add(descLog);
        paramLogBuilder.append("Request Args    : \n{}\n");
        logObjs.add(requestArgJsonStr);
        paramLogBuilder.append("=============================================Result-Field=============================================\n");

        Object proceed;
        try {
            proceed = invocation.proceed();
            long endTime = System.currentTimeMillis();
            Boolean enableResultLog = Optional.ofNullable(myLog).map(MyLog::enableResultLog).orElse(true);
            paramLogBuilder.append("Cost            : {}ms\n");
            logObjs.add(endTime-startTime);
            if (BooleanUtil.isTrue(enableResultLog)) {
                String jsonStr = MyJsonUtils.toJsonString(proceed);
                paramLogBuilder.append("Result          : \n{}\n");
                logObjs.add(jsonStr);
            }
            paramLogBuilder.append("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<--The--End--<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
            Object[] objectArr = logObjs.toArray(new Object[]{});
            log.info(paramLogBuilder.toString(),objectArr);
        } catch (Throwable e) {
            long endTimeError = System.currentTimeMillis();
            paramLogBuilder.append("Cost            : {}ms\n");
            logObjs.add(endTimeError-startTime);
            paramLogBuilder.append("ErrorStackTrace : \n{}\n");
            paramLogBuilder.append("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<--The--End--<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
            logObjs.add(ThrowableUtil.getStackTrace(e));
            Object[] objectArr = logObjs.toArray(new Object[]{});
            log.error(paramLogBuilder.toString(),objectArr);
            throw new RuntimeException(e);
        }
        return proceed;
    }
}

    